package com.wideplay.warp.clustering;

import com.google.inject.*;
import com.wideplay.warp.clustering.swarmcache.SwarmBindingSupport;
import com.wideplay.warp.clustering.oscache.OsCacheBindingSupport;

import java.lang.annotation.Annotation;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;

/**
 * Created with IntelliJ IDEA.
 * On: 23/06/2007
 *
 * @author Dhanji R. Prasanna
 * @since 1.0
 */
class DataGridModule extends AbstractModule {
    private final List<Class<? extends Annotation>> clusters = new ArrayList<Class<? extends Annotation>>();
    private final ClusterCacheVendor cacheVendor;


    public DataGridModule(ClusterCacheVendor cacheVendor) {
        this.cacheVendor = cacheVendor;
    }

    protected void configure() {
        //freeze config
        List<Class<? extends Annotation>> clusters = Collections.unmodifiableList(this.clusters);

        //bind common service artifacts
        bind(DataGridService.SpacesListWrapper.class).toInstance(new DataGridService.SpacesListWrapper(clusters));


        switch (cacheVendor) {
            case SWARMCACHE:
                SwarmBindingSupport.bindClusterService(binder());
                break;
            case OSCACHE:
                OsCacheBindingSupport.bindClusterService(binder());
                break;
        }


        //bind multiple cluster spaces?
        final boolean isMultipleClusterSpaces = clusters.size() > 0;
        if (!isMultipleClusterSpaces) {
            bindScope(GridScoped.class, GridScope.namedClusterScope(GridScoped.class));

            switch (cacheVendor) {
                case SWARMCACHE:
                    SwarmBindingSupport.bindObjectCluster(binder());
                    break;
                case OSCACHE:
                    OsCacheBindingSupport.bindObjectCluster(binder());
                    break;
            }

        } else {
            bindMultipleClusterSpaces(clusters);
        }

    }

    private void bindMultipleClusterSpaces(List<Class<? extends Annotation>> clusters) {
        //multiple clusters' artifacts are bound by the scoping annotation itself (required to be a binding annotation too)

        for (Class<? extends Annotation> cluster : clusters) {

            if (!cluster.isAnnotationPresent(BindingAnnotation.class))
                addError("Custom cluster-scope annotation must itself *also* be annotated with @BindingAnnotation" +
                        " (otherwise there's no way to distinguish between ClusterServices--if you're using only " +
                        "1 cluster do not specify a .cluster() argument): " + cluster);

            if (!cluster.isAnnotationPresent(ScopeAnnotation.class))
                addError("Custom cluster-scope annotation must itself be annotated with @ScopeAnnotation: " + cluster);

            //bind scope
            bindScope(cluster, GridScope.namedClusterScope(cluster));

            //bind the various cluster artifacts at specified binding annotations (instead of globally)

            switch (cacheVendor) {
                case SWARMCACHE:
                    SwarmBindingSupport.bindObjectCluster(binder(), cluster);
                    break;
                case OSCACHE:
                    OsCacheBindingSupport.bindObjectCluster(binder(), cluster);
                    break;
            }
        }
    }

    /**
     *  Adds a new cluster space to the guice scope-chain
     * @param clusterScopeAnnotation A guice scoping annotation to use for resolving between available clusters
     */
    void addClusterSpace(Class<? extends Annotation> clusterScopeAnnotation) {
        clusters.add(clusterScopeAnnotation);
    }

    public static enum ClusterCacheVendor {
        SWARMCACHE, JBOSSCACHE, TANGOSOL, OSCACHE, 
    }
}
